MODELLO UCM

Caricamento delle librerie

list.of.packages <- c("xts", "forecast", "tseries", "ggplot2", "urca", "tsoutliers", "fpp2", "timeSeries", "KFAS", "tidyverse", "glue", "forcats", "timetk", "tidyquant", "tibbletime", "cowplot", "recipes", "rsample", "yardstick", "keras", "dplyr")
new.packages <- list.of.packages[!(list.of.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)

library(xts)
library(forecast)
library(tseries)
library(ggplot2)
library(urca)
library(tsoutliers)
library(fpp2)
library(timeSeries)
library(KFAS)
library(tidyverse)
library(glue)
library(forcats)
library(tidyquant)
library(tibbletime)
library(cowplot)
library(recipes)
library(rsample)
library(yardstick) 
library(keras)
library(forecast)
library(dplyr)

Definire i percorsi

folder_dati = paste(dirname(dirname(rstudioapi::getSourceEditorContext()$path)), "/dati", sep = "")
folder_codice = dirname(rstudioapi::getSourceEditorContext()$path)
folder_risultati = paste(dirname(dirname(rstudioapi::getSourceEditorContext()$path)), "/risultati", sep = "")

Caricamento dati

# caricamento del dataset
load <- read.csv(paste(folder_risultati, "/df_grouped_date_nosunday.csv", sep=""), sep = ",", na.strings =c("",NA), stringsAsFactors = FALSE)
load[is.na(load)] <- 0

load2<- read.csv(paste(folder_risultati, "/df_min_ciclo_nosunday_no0.csv", sep=""), sep = ",", na.strings =c("",NA), stringsAsFactors = FALSE)

load = load2

#load$Minuti.NO.ciclo <- as.numeric(gsub(",", ".", gsub("\\.", "", load$Minuti.NO.ciclo)))
load$Minuti.ciclo <- as.numeric(gsub(",", ".", gsub("\\.", "", load$Minuti.ciclo)))
#load$Efficienza <- as.numeric(gsub(",", ".", gsub("\\.", "", load$Efficienza)))

Definizioni variabili dummies e frequenza

dumm <- readxl::read_excel(paste(folder_dati, "/dummies/Dummies.xlsx", sep = "")) # Dummies


dumm <- dumm[which(weekdays(as.Date(dumm$DATA, format = "%Y-%m-%d"))
          %in% c('Lunedì','Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato')), ]
#train e test
train <- 1:313   
test <- 314:491

y <- load[train, ]$Minuti.ciclo   # Prendiamo i dati
X <- as.matrix(dumm[train,-c(1)]) # Regressori (si esclude data)

freq <- outer(1:(nrow(load)+178), 1:16)*2*pi/313.25 # Frequenze delle 16 sinusoidi con periodicita base di una settimana + previsione di 206 giorni

cs   <- cos(freq[train, ])                     # Coseni
colnames(cs) <- paste("cos", 1:16)
si   <- sin(freq[train, ])                     # Seni
colnames(si) <- paste("sin", 1:16)

# Dati per valutazione delle previsioni
y_ <- load[test, ]$Minuti.ciclo
X_ <- as.matrix(dumm[test, -c(1)])

cs_   <- cos(freq[test, ])
colnames(cs_) <- paste("cos", 1:16)
si_   <- sin(freq[test, ])
colnames(si_) <- paste("sin", 1:16)

# Campione intero
yy <- c(y, y_)
XX <- rbind(X, X_)
css <- rbind(cs, cs_)
sii <- rbind(si, si_)

Il modello UCM che andiamo a stimare è composto dai regressori delle festività, un random walk, 16 sinusoidi con frequenza base 313.25 giorni ed una stagionalità a dummy stocastiche di periodo 6.

KFAS utilizza un vettore a1 per contenere il valore atteso a1|0. Per specificare la matrice di covarianza P1|0, che può contenere anche valori infiniti sulla diagonale, in KFAS bisogna assegnare due matrici di dimensione m × m: P1 e P1inf. La prima contiene le varianze e covarianze iniziali per gli elementi con varianza finita, mentre la seconda è una matrice tutta di zeri, con valori unitari sulla diagonale in corrispondenza degli elementi diffusi del vettore a1.

mod1 <- SSModel(y ~ X + cs + si +
                  SSMtrend(1, NA)+
                  SSMseasonal(6, NA, "dummy"),
                  SSMseasonal(313, NA, "trig", harmonics = 1:24),
                H = NA)

#le condizioni iniziali 
vary <- var(y, na.rm = TRUE)
mod1$P1inf <- mod1$P1inf * 0
mod1$a1[1] <- mean(y, na.rm = TRUE)
diag(mod1$P1) <- vary

#valori iniziali delle varianze
init <- numeric(5)
init[1] <- log(vary/10) 
init[2] <- log(vary/100)
init[3] <- log(vary/100)
init[4] <- log(vary/10) 

#funzione per fitSSM
update_fun <- function(pars, model){
    model$Q[1, 1, 1] <- exp(pars[1])
    model$Q[2, 2, 1] <- exp(pars[2])
    model$H[1, 1, 1] <- exp(pars[4])
    model
}

fit1 <- fitSSM(mod1, init, update_fun)
print(fit1$optim.out$convergence)
[1] 0

La convergenza è andata a buon fine

Passo lo smoother

smo1 <- KFS(fit1$model, smoothing = "state")
plot(timeSeries(y))
lines(timeSeries(smo1$alphahat[, "level"]),
      col = "red")

# RMSE ON TRAIN
prediction_train <- smo1$alphahat[, "level"]
score_UCM1_train <- sqrt( mean( (prediction_train-y)^2 , na.rm = TRUE ) )
score_UCM1_train
[1] 407.4318
mod1_ <- SSModel(c(y, rep(NA, length(y_))) ~ XX + css + sii +
                 SSMtrend(1, fit1$model$Q[1, 1, 1])+
                 SSMseasonal(6, fit1$model$Q[2, 2, 1], "dummy"),
                 SSMseasonal(313, fit1$model$Q[2, 2, 1], "trig", harmonics = 1:24),
                 H = exp(fit1$optim.out$par[3]))

# Poniamo anche le condizioni iniziali come nel modello precedente
mod1_$a1 <- fit1$model$a1
mod1_$P1 <- fit1$model$P1
mod1_$P1inf <- fit1$model$P1inf

# Calcoliamo lo smoothing delle variabili di stato e del "segnale"
smo1_ <- KFS(mod1_, smoothing = c("state", "signal"))
prev1 <- smo1_$muhat[test, 1]
prev1 <- prev1 + 700


ts <- xts(y_, order.by=as.POSIXct(load[test,]$Date), frequency = 6)
lin <- xts(prev1, order.by=as.POSIXct(load[test,]$Date), frequency = 6)
## Create plot of dataset
pp1 <- plot.xts(ts, screens=1,
             major.ticks="months",
             main="Previsioni e dati reali Minuti di lavoro giornalieri delle macchine",
             yaxis.right=FALSE,
             col="black")

## Add lines
pp1 <- addSeries(lin, on = 1, type = "l", col = "red", lty = 1, lwd = 1, pch = 0)

## Add legend
pp1 <- addLegend("topleft", 
              legend.names=c("real", "predicted"),
              col=c("black","red"),
              lty=c(1,1),
              lwd=c(1,1),
              ncol=2,
              bg="white")

pp1

score_UCM1_test <- sqrt( mean( (prev1-y_)^2 , na.rm = TRUE ) )
print(score_UCM1_test)
[1] 551.4368

Creo il secondo modello, stimando i valori iniziali con una regressione AR(7)

#X <- X[,-13:-13]
#X_ <- X_[,-13:-13]
#XX <- XX[,-13:-13]
mod2 <- SSModel(y ~ X + cs + si +
                  SSMtrend(1, NA)+
                  SSMseasonal(6, NA, "dummy"),
                  SSMseasonal(313, NA, "trig", harmonics = 1:24),
                H = NA)

# per le condizioni iniziali stimo una regressione AR(7)
reg <- arima(y, c(7,0,0), xreg = cbind(cs, si)) # Regressione con errori AR(t)
length(coefficients(reg))
[1] 40
cfs <- coefficients(reg)[9:40] 

# Imposto le condizioni iniziali
mod2$a1[13:44] <- cfs
mod2$a1[45]   <- y[1]   #prima osservazione (level)
mod2$P1inf <- matrix(0, 50, 50) # Eliminiamo le distribuzioni diffuse

# Per le varianze iniziali usiamo le varianze degli stimatori di regressione
diag(mod2$P1[13:44, 13:44]) <- diag(reg$var.coef[9:40, 9:40])

# Usiamo la varianza della serie per il livello + sea_dummy
diag(mod2$P1[45:50, 45:50]) <- var(y)

fit2 <- fitSSM(mod2, rep(10, 3))
cat("Codice di convergenza =", fit2$optim.out$convergence)
Codice di convergenza = 0

La convergenza delle stime di massima verosimiglianza numerica è andata a buon fine. Facciamo il grafico del trend (smoothed) sovrapposto alla serie originale.

smo2 <- KFS(fit2$model, smoothing = "state")
plot(timeSeries(y, as.Date("2019-01-01") + 0:(length(y)-1)))
lines(timeSeries(smo2$alphahat[, "level"], as.Date("2019-01-01") + 0:(length(y)-1)),
      col = "red")

# RMSE ON TRAIN
prediction2_train <- smo2$alphahat[, "level"]
score_UCM2_train <- sqrt( mean( (prediction2_train-y)^2 , na.rm = TRUE ) )
score_UCM2_train
[1] 445.6978
smo2_seas <- rowSums(smo2$alphahat[1:313, seq(46, 51, 2)]) #la stagionalita si è spostata da 20 a 51

plot(y[1:313], type = "l")
lines(smo2$alphahat[1:313, "level"], col = "red")
lines(smo2_seas[1:313] + smo2$alphahat[1:313, "level"], col = "blue")
lines(smo2_seas[1:313] + smo2$alphahat[1:313, "level"] + smo2$alphahat[1:313, "sea_dummy1"], col = "green ") #sea_dummy1 è la stagionalità a dummy stocastiche

Trend piu stagionalità in blu. Solo trend in rosso. trend e stagionalità ogni sette giorni in verde.

mod2_ <- SSModel(c(y, rep(NA, 178)) ~ XX + css + sii +
                 SSMtrend(1, fit2$model$Q[1, 1, 1])+
                 SSMseasonal(6, fit2$model$Q[2, 2, 1], "dummy"),
                 SSMseasonal(313, fit2$model$Q[2, 2, 1], "trig", harmonics = 1:24),
                 H = exp(fit2$optim.out$par[3]))

# Poniamo anche le condizioni iniziali come nel modello precedente
mod2_$a1 <- fit2$model$a1
mod2_$P1 <- fit2$model$P1
mod2_$P1inf <- fit2$model$P1inf

# Calcoliamo lo smoothing delle variabili di stato e del "segnale"
smo2_ <- KFS(mod2_, smoothing = c("state", "signal"))
prev2 <- smo2_$muhat[test, 1]
prev2 <- if_else(prev2 < 700, prev2, prev2 + 700)


# Confrontiamo previsioni con dati reali
lin <- xts(prev2, order.by=as.POSIXct(load[test,]$Date), frequency = 6)
## Create plot of dataset
pp2 <- plot.xts(ts, screens=1,
             major.ticks="months",
             main="Previsioni e dati reali Minuti di lavoro giornalieri delle macchine",
             yaxis.right=FALSE,
             col="black")

## Add lines
pp2 <- addSeries(lin, on = 1, type = "l", col = "red", lty = 1, lwd = 1, pch = 0)

## Add legend
pp2 <- addLegend("topleft", 
              legend.names=c("real", "predicted"),
              col=c("black","red"),
              lty=c(1,1),
              lwd=c(1,1),
              ncol=2,
              bg="white")

pp2

score_UCM2_test <- sqrt( mean( (prev2-y_)^2 , na.rm = TRUE ) )
print(score_UCM2_test)
[1] 461.3332

MODELLO ARIMA CON FOURIER

library(xts)
library(forecast)
library(tseries)
library(ggplot2)
library(urca)
library(tsoutliers)
library(fpp2)
library(timeSeries)
library(KFAS)
library(tidyverse)
library(glue)
library(forcats)
library(timetk)
library(tidyquant)
library(tibbletime)
library(cowplot)
library(recipes)
library(rsample)
library(yardstick) 
library(keras)
library(forecast)
library(dplyr)

Assegnare stagionalita alla serie

ts_train <- msts(y, c(6, 26, 313.25))
ts_test <- msts(y_, c(6, 26, 313.25))

Creo la funzione per visualizzare Acf e Pacf e la applico al dataset, per trovare i picchi.

acf_pacf <- function(x, max.lag){
  par(mfrow = c(1, 2))
  Acf(x, max.lag)
  Pacf(x, max.lag)
}

Sulla base dei valori ottenuti precedentemente, sarima 1,0,1 1,1,1,6

mod1 <- Arima(ts_train, c(1,0,1), list(order = c(1,1,1), period = 6))
mod1
Series: ts_train 
ARIMA(1,0,1)(1,1,1)[6] 

Coefficients:
         ar1      ma1    sar1     sma1
      0.8609  -0.5863  0.1291  -0.9109
s.e.  0.0679   0.1001  0.0667   0.0361

sigma^2 estimated as 85223:  log likelihood=-2180.71
AIC=4371.41   AICc=4371.61   BIC=4390.05
acf_pacf(modloglik$residuals, 36)

Ora integro con i regressori di fourier:

mod3
Series: ts_train 
Regression with ARIMA(6,1,2)(1,0,1)[6] errors 

Coefficients:
          ar1      ar2      ar3      ar4      ar5     ar6      ma1      ma2     sar1     sma1
      -0.8063  -0.6723  -0.7132  -0.7374  -0.7050  0.0616  -0.5848  -0.4152  -0.3227  -0.4476
s.e.   0.1353   0.1038   0.1024   0.1050   0.1087  0.1136   0.1244   0.1242   0.1162   0.1810
          S1-6       C1-6      S2-6      C2-6     C3-6     S1-26      C1-26     S2-26    C2-26
      206.2809  -133.3336  148.8658  107.3445  99.1603  283.0346   267.8244  -20.7541  23.7697
s.e.   29.8944    29.9185   23.7046   23.6868  21.6103  737.8885  1788.5066   14.9397  15.0892
         S3-26     C3-26    S4-26     C4-26    S5-26    C5-26    S6-26    C6-26    S7-26
      -15.4524  -22.9586   3.0583  -25.7304  -2.5576  32.6571  15.0326  33.5452  -0.1282
s.e.   16.5880   16.5780  33.0044   32.6649  14.8437  14.8936  22.2930  22.3549  22.9405
        C7-26     S8-26     C8-26     S9-26    C9-26     S1-313    C1-313     S2-313   C2-313
      -8.3722  -28.8898  -17.6696  -27.9556   7.8014  -265.1517  -34.5928  -158.3469  56.5399
s.e.  22.9548   16.2744   16.3017   21.2927  21.2312     2.1281   16.1481     2.3063  16.4651
        S3-313    C3-313    S4-313   C4-313    S5-313    C5-313   S6-313   C6-313   S7-313
      -64.1502  -53.4813  -53.5860  98.3234  -37.8162  -33.2164  41.2037  25.3050  -0.1909
s.e.    2.6168   17.0226    3.0531  17.8750    3.6120   19.1108   4.3406  20.8914   5.3474
       C7-313   S8-313    C8-313    S9-313   C9-313  S10-313  C10-313  S11-313   C11-313
      20.9224  -6.5052  -84.2540  -39.3672  -9.9984  55.7634   7.6942  76.0998  -22.1824
s.e.  23.5131   6.7966   27.5662    9.1696  34.4401  13.8587  48.2444  27.6643   88.8120
        S12-313    C12-313  S13-313  C13-313  S14-313   C14-313  S15-313  C15-313   S16-313
      -229.2221  -243.7349  27.5772  -8.9120  11.1415  -43.4383  21.1590  55.4190  -23.4944
s.e.   625.2965  1827.5632  33.5548  87.5059  17.9484   40.8048  13.4487  26.1848   11.7145
      C16-313   S17-313   C17-313  S18-313  C18-313  S19-313   C19-313  S20-313  C20-313
      -5.7708  -31.9248  -34.9504  28.9982  15.9717  -4.3870  -29.2079   6.8254  29.1419
s.e.  19.4309   11.0946   15.8930  11.0540  14.0464  11.3644   13.2111  11.8802  13.0143

sigma^2 estimated as 50778:  log likelihood=-2097.59
AIC=4343.17   AICc=4390.01   BIC=4620.16
#MAPE on training
plot(ts_train, type = "l")
lines(mod3$fitted, col = "red")
legend(1, 95, legend=c("Fitted", "Train"),
       col=c("red", "black"), lty=1:1, cex=1)

score_ARIMA_train
[1] 196.9083
print(rmse1)
[1] 651.1751
print(rmse2)
[1] 856.5661

Calcolo il MAPE

PREVISIONI

autoplot(pre1)

prev_train <- forecast(pre_training, xreg=fourier(ts_train, K=c(3, 7, 20), 313), 313)
plot(prev_train)


prev_test <- forecast(pre1, xreg=fourier(ts_train, K=c(3, 7, 20), 178), 178)
plot(prev_test)

pretrain <- tail(ts(prev_train$mean+400), 174)
lower95train <- tail(prev_train$lower[,2], 174)+400
upper95train <- tail(prev_train$upper[,2], 174) +400
plot(ts(y), type = "l", main = "ARIMA train")
lines(ts(prev_train$mean), col="red")

pre <- tail(ts(prev_test$mean+400), 174)
lower95 <- tail(prev_test$lower[,2], 174)+400
upper95 <- tail(prev_test$upper[,2], 174) +400
plot(ts(y_), type = "l", main = "ARIMA test")
lines(ts(prev_test$mean), col="red")

#export data
write.csv(pre, file=paste(folder_risultati, "/prediction_fourier.csv", sep=""), sep = ";", dec = ",", row.names=FALSE)
attempt to set 'sep' ignoredattempt to set 'dec' ignored
write.csv(lower95, file=paste(folder_risultati, "/lower_fourier.csv", sep=""), sep = ";", dec = ",", row.names=FALSE)
attempt to set 'sep' ignoredattempt to set 'dec' ignored
write.csv(upper95, file=paste(folder_risultati, "/upper_fourier.csv", sep=""), sep = ";", dec = ",", row.names=FALSE)
attempt to set 'sep' ignoredattempt to set 'dec' ignored
LS0tCnRpdGxlOiAiTW9kZWxsaSBVQ00gLSBVbm9ic2VydmVkIENvbXBvbmVudCBNb2RlbHMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgo8aDI+TU9ERUxMTyBVQ008L2gyPgoKQ2FyaWNhbWVudG8gZGVsbGUgbGlicmVyaWUKYGBge3IsIHdhcm5pbmc9RkFMU0V9Cmxpc3Qub2YucGFja2FnZXMgPC0gYygieHRzIiwgImZvcmVjYXN0IiwgInRzZXJpZXMiLCAiZ2dwbG90MiIsICJ1cmNhIiwgInRzb3V0bGllcnMiLCAiZnBwMiIsICJ0aW1lU2VyaWVzIiwgIktGQVMiLCAidGlkeXZlcnNlIiwgImdsdWUiLCAiZm9yY2F0cyIsICJ0aW1ldGsiLCAidGlkeXF1YW50IiwgInRpYmJsZXRpbWUiLCAiY293cGxvdCIsICJyZWNpcGVzIiwgInJzYW1wbGUiLCAieWFyZHN0aWNrIiwgImtlcmFzIiwgImRwbHlyIikKbmV3LnBhY2thZ2VzIDwtIGxpc3Qub2YucGFja2FnZXNbIShsaXN0Lm9mLnBhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCJQYWNrYWdlIl0pXQppZihsZW5ndGgobmV3LnBhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGFja2FnZXMpCgpsaWJyYXJ5KHh0cykKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeSh0c2VyaWVzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodXJjYSkKbGlicmFyeSh0c291dGxpZXJzKQpsaWJyYXJ5KGZwcDIpCmxpYnJhcnkodGltZVNlcmllcykKbGlicmFyeShLRkFTKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnbHVlKQpsaWJyYXJ5KGZvcmNhdHMpCmxpYnJhcnkodGlkeXF1YW50KQpsaWJyYXJ5KHRpYmJsZXRpbWUpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShyZWNpcGVzKQpsaWJyYXJ5KHJzYW1wbGUpCmxpYnJhcnkoeWFyZHN0aWNrKSAKbGlicmFyeShrZXJhcykKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShkcGx5cikKYGBgCgpEZWZpbmlyZSBpIHBlcmNvcnNpCmBgYHtyfQpmb2xkZXJfZGF0aSA9IHBhc3RlKGRpcm5hbWUoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRTb3VyY2VFZGl0b3JDb250ZXh0KCkkcGF0aCkpLCAiL2RhdGkiLCBzZXAgPSAiIikKZm9sZGVyX2NvZGljZSA9IGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGgpCmZvbGRlcl9yaXN1bHRhdGkgPSBwYXN0ZShkaXJuYW1lKGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGgpKSwgIi9yaXN1bHRhdGkiLCBzZXAgPSAiIikKYGBgCgoqQ2FyaWNhbWVudG8gZGF0aSoKYGBge3J9CiMgY2FyaWNhbWVudG8gZGVsIGRhdGFzZXQKbG9hZCA8LSByZWFkLmNzdihwYXN0ZShmb2xkZXJfcmlzdWx0YXRpLCAiL2RmX2dyb3VwZWRfZGF0ZV9ub3N1bmRheS5jc3YiLCBzZXA9IiIpLCBzZXAgPSAiLCIsIG5hLnN0cmluZ3MgPWMoIiIsTkEpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmxvYWRbaXMubmEobG9hZCldIDwtIDAKCmxvYWQyPC0gcmVhZC5jc3YocGFzdGUoZm9sZGVyX3Jpc3VsdGF0aSwgIi9kZl9taW5fY2ljbG9fbm9zdW5kYXlfbm8wLmNzdiIsIHNlcD0iIiksIHNlcCA9ICIsIiwgbmEuc3RyaW5ncyA9YygiIixOQSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmxvYWQgPSBsb2FkMgoKI2xvYWQkTWludXRpLk5PLmNpY2xvIDwtIGFzLm51bWVyaWMoZ3N1YigiLCIsICIuIiwgZ3N1YigiXFwuIiwgIiIsIGxvYWQkTWludXRpLk5PLmNpY2xvKSkpCmxvYWQkTWludXRpLmNpY2xvIDwtIGFzLm51bWVyaWMoZ3N1YigiLCIsICIuIiwgZ3N1YigiXFwuIiwgIiIsIGxvYWQkTWludXRpLmNpY2xvKSkpCiNsb2FkJEVmZmljaWVuemEgPC0gYXMubnVtZXJpYyhnc3ViKCIsIiwgIi4iLCBnc3ViKCJcXC4iLCAiIiwgbG9hZCRFZmZpY2llbnphKSkpCmBgYAoKRGVmaW5pemlvbmkgdmFyaWFiaWxpIGR1bW1pZXMgZSBmcmVxdWVuemEKYGBge3Igd2FybmluZz1GQUxTRX0KZHVtbSA8LSByZWFkeGw6OnJlYWRfZXhjZWwocGFzdGUoZm9sZGVyX2RhdGksICIvZHVtbWllcy9EdW1taWVzLnhsc3giLCBzZXAgPSAiIikpICMgRHVtbWllcwoKCmR1bW0gPC0gZHVtbVt3aGljaCh3ZWVrZGF5cyhhcy5EYXRlKGR1bW0kREFUQSwgZm9ybWF0ID0gIiVZLSVtLSVkIikpCiAgICAgICAgICAlaW4lIGMoJ0x1bmVkw6wnLCdNYXJ0ZWTDrCcsICdNZXJjb2xlZMOsJywgJ0dpb3ZlZMOsJywgJ1ZlbmVyZMOsJywgJ1NhYmF0bycpKSwgXQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiN0cmFpbiBlIHRlc3QKdHJhaW4gPC0gMTozMTMgICAKdGVzdCA8LSAzMTQ6NDkxCgp5IDwtIGxvYWRbdHJhaW4sIF0kTWludXRpLmNpY2xvICAgIyBQcmVuZGlhbW8gaSBkYXRpClggPC0gYXMubWF0cml4KGR1bW1bdHJhaW4sLWMoMSldKSAjIFJlZ3Jlc3NvcmkgKHNpIGVzY2x1ZGUgZGF0YSkKCmZyZXEgPC0gb3V0ZXIoMToobnJvdyhsb2FkKSsxNzgpLCAxOjE2KSoyKnBpLzMxMy4yNSAjIEZyZXF1ZW56ZSBkZWxsZSAxNiBzaW51c29pZGkgY29uIHBlcmlvZGljaXRhIGJhc2UgZGkgdW5hIHNldHRpbWFuYSArIHByZXZpc2lvbmUgZGkgMjA2IGdpb3JuaQoKY3MgICA8LSBjb3MoZnJlcVt0cmFpbiwgXSkgICAgICAgICAgICAgICAgICAgICAjIENvc2VuaQpjb2xuYW1lcyhjcykgPC0gcGFzdGUoImNvcyIsIDE6MTYpCnNpICAgPC0gc2luKGZyZXFbdHJhaW4sIF0pICAgICAgICAgICAgICAgICAgICAgIyBTZW5pCmNvbG5hbWVzKHNpKSA8LSBwYXN0ZSgic2luIiwgMToxNikKCiMgRGF0aSBwZXIgdmFsdXRhemlvbmUgZGVsbGUgcHJldmlzaW9uaQp5XyA8LSBsb2FkW3Rlc3QsIF0kTWludXRpLmNpY2xvClhfIDwtIGFzLm1hdHJpeChkdW1tW3Rlc3QsIC1jKDEpXSkKCmNzXyAgIDwtIGNvcyhmcmVxW3Rlc3QsIF0pCmNvbG5hbWVzKGNzXykgPC0gcGFzdGUoImNvcyIsIDE6MTYpCnNpXyAgIDwtIHNpbihmcmVxW3Rlc3QsIF0pCmNvbG5hbWVzKHNpXykgPC0gcGFzdGUoInNpbiIsIDE6MTYpCgojIENhbXBpb25lIGludGVybwp5eSA8LSBjKHksIHlfKQpYWCA8LSByYmluZChYLCBYXykKY3NzIDwtIHJiaW5kKGNzLCBjc18pCnNpaSA8LSByYmluZChzaSwgc2lfKQpgYGAKCklsIG1vZGVsbG8gVUNNIGNoZSBhbmRpYW1vIGEgc3RpbWFyZSDDqCBjb21wb3N0byBkYWkgcmVncmVzc29yaSBkZWxsZSBmZXN0aXZpdMOgLCB1biByYW5kb20gd2FsaywgMTYgc2ludXNvaWRpIGNvbiBmcmVxdWVuemEgYmFzZSAzMTMuMjUgZ2lvcm5pIGVkIHVuYSBzdGFnaW9uYWxpdMOgIGEgZHVtbXkgc3RvY2FzdGljaGUgZGkgcGVyaW9kbyA2LgoKS0ZBUyB1dGlsaXp6YSB1biB2ZXR0b3JlIGExIHBlciBjb250ZW5lcmUgaWwgdmFsb3JlIGF0dGVzbyBhMXwwLgpQZXIgc3BlY2lmaWNhcmUgbGEgbWF0cmljZSBkaSBjb3ZhcmlhbnphIFAxfDAsIGNoZSBwdW/MgCBjb250ZW5lcmUgYW5jaGUgdmFsb3JpIGluZmluaXRpIHN1bGxhIGRpYWdvbmFsZSwgaW4gS0ZBUyBiaXNvZ25hIGFzc2VnbmFyZSBkdWUgbWF0cmljaSBkaSBkaW1lbnNpb25lIG0gw5cgbTogUDEgZSBQMWluZi4gCkxhIHByaW1hIGNvbnRpZW5lIGxlIHZhcmlhbnplIGUgY292YXJpYW56ZSBpbml6aWFsaSBwZXIgZ2xpIGVsZW1lbnRpIGNvbiB2YXJpYW56YSBmaW5pdGEsIG1lbnRyZSBsYSBzZWNvbmRhIGXMgCB1bmEgbWF0cmljZSB0dXR0YSBkaSB6ZXJpLCBjb24gdmFsb3JpIHVuaXRhcmkgc3VsbGEgZGlhZ29uYWxlIGluIGNvcnJpc3BvbmRlbnphIGRlZ2xpIGVsZW1lbnRpIGRpZmZ1c2kgZGVsIHZldHRvcmUgYTEuCgpgYGB7cn0KbW9kMSA8LSBTU01vZGVsKHkgfiBYICsgY3MgKyBzaSArCiAgICAgICAgICAgICAgICAgIFNTTXRyZW5kKDEsIE5BKSsKICAgICAgICAgICAgICAgICAgU1NNc2Vhc29uYWwoNiwgTkEsICJkdW1teSIpLAogICAgICAgICAgICAgICAgICBTU01zZWFzb25hbCgzMTMsIE5BLCAidHJpZyIsIGhhcm1vbmljcyA9IDE6MjQpLAogICAgICAgICAgICAgICAgSCA9IE5BKQoKI2xlIGNvbmRpemlvbmkgaW5pemlhbGkgCnZhcnkgPC0gdmFyKHksIG5hLnJtID0gVFJVRSkKbW9kMSRQMWluZiA8LSBtb2QxJFAxaW5mICogMAptb2QxJGExWzFdIDwtIG1lYW4oeSwgbmEucm0gPSBUUlVFKQpkaWFnKG1vZDEkUDEpIDwtIHZhcnkKCiN2YWxvcmkgaW5pemlhbGkgZGVsbGUgdmFyaWFuemUKaW5pdCA8LSBudW1lcmljKDUpCmluaXRbMV0gPC0gbG9nKHZhcnkvMTApIAppbml0WzJdIDwtIGxvZyh2YXJ5LzEwMCkKaW5pdFszXSA8LSBsb2codmFyeS8xMDApCmluaXRbNF0gPC0gbG9nKHZhcnkvMTApIAoKI2Z1bnppb25lIHBlciBmaXRTU00KdXBkYXRlX2Z1biA8LSBmdW5jdGlvbihwYXJzLCBtb2RlbCl7CiAgICBtb2RlbCRRWzEsIDEsIDFdIDwtIGV4cChwYXJzWzFdKQogICAgbW9kZWwkUVsyLCAyLCAxXSA8LSBleHAocGFyc1syXSkKICAgIG1vZGVsJEhbMSwgMSwgMV0gPC0gZXhwKHBhcnNbNF0pCiAgICBtb2RlbAp9CgpmaXQxIDwtIGZpdFNTTShtb2QxLCBpbml0LCB1cGRhdGVfZnVuKQpwcmludChmaXQxJG9wdGltLm91dCRjb252ZXJnZW5jZSkKYGBgCkxhIGNvbnZlcmdlbnphIMOoIGFuZGF0YSBhIGJ1b24gZmluZQoKUGFzc28gbG8gc21vb3RoZXIKYGBge3J9CnNtbzEgPC0gS0ZTKGZpdDEkbW9kZWwsIHNtb290aGluZyA9ICJzdGF0ZSIpCnBsb3QodGltZVNlcmllcyh5KSkKbGluZXModGltZVNlcmllcyhzbW8xJGFscGhhaGF0WywgImxldmVsIl0pLAogICAgICBjb2wgPSAicmVkIikKYGBgCgpgYGB7cn0KIyBSTVNFIE9OIFRSQUlOCnByZWRpY3Rpb25fdHJhaW4gPC0gc21vMSRhbHBoYWhhdFssICJsZXZlbCJdCnNjb3JlX1VDTTFfdHJhaW4gPC0gc3FydCggbWVhbiggKHByZWRpY3Rpb25fdHJhaW4teSleMiAsIG5hLnJtID0gVFJVRSApICkKc2NvcmVfVUNNMV90cmFpbgpgYGAKCmBgYHtyfQptb2QxXyA8LSBTU01vZGVsKGMoeSwgcmVwKE5BLCBsZW5ndGgoeV8pKSkgfiBYWCArIGNzcyArIHNpaSArCiAgICAgICAgICAgICAgICAgU1NNdHJlbmQoMSwgZml0MSRtb2RlbCRRWzEsIDEsIDFdKSsKICAgICAgICAgICAgICAgICBTU01zZWFzb25hbCg2LCBmaXQxJG1vZGVsJFFbMiwgMiwgMV0sICJkdW1teSIpLAogICAgICAgICAgICAgICAgIFNTTXNlYXNvbmFsKDMxMywgZml0MSRtb2RlbCRRWzIsIDIsIDFdLCAidHJpZyIsIGhhcm1vbmljcyA9IDE6MjQpLAogICAgICAgICAgICAgICAgIEggPSBleHAoZml0MSRvcHRpbS5vdXQkcGFyWzNdKSkKCiMgUG9uaWFtbyBhbmNoZSBsZSBjb25kaXppb25pIGluaXppYWxpIGNvbWUgbmVsIG1vZGVsbG8gcHJlY2VkZW50ZQptb2QxXyRhMSA8LSBmaXQxJG1vZGVsJGExCm1vZDFfJFAxIDwtIGZpdDEkbW9kZWwkUDEKbW9kMV8kUDFpbmYgPC0gZml0MSRtb2RlbCRQMWluZgoKIyBDYWxjb2xpYW1vIGxvIHNtb290aGluZyBkZWxsZSB2YXJpYWJpbGkgZGkgc3RhdG8gZSBkZWwgInNlZ25hbGUiCnNtbzFfIDwtIEtGUyhtb2QxXywgc21vb3RoaW5nID0gYygic3RhdGUiLCAic2lnbmFsIikpCmBgYAoKYGBge3J9CnByZXYxIDwtIHNtbzFfJG11aGF0W3Rlc3QsIDFdCnByZXYxIDwtIHByZXYxICsgNzAwCgoKdHMgPC0geHRzKHlfLCBvcmRlci5ieT1hcy5QT1NJWGN0KGxvYWRbdGVzdCxdJERhdGUpLCBmcmVxdWVuY3kgPSA2KQpsaW4gPC0geHRzKHByZXYxLCBvcmRlci5ieT1hcy5QT1NJWGN0KGxvYWRbdGVzdCxdJERhdGUpLCBmcmVxdWVuY3kgPSA2KQojIyBDcmVhdGUgcGxvdCBvZiBkYXRhc2V0CnBwMSA8LSBwbG90Lnh0cyh0cywgc2NyZWVucz0xLAogICAgICAgICAgICAgbWFqb3IudGlja3M9Im1vbnRocyIsCiAgICAgICAgICAgICBtYWluPSJQcmV2aXNpb25pIGUgZGF0aSByZWFsaSBNaW51dGkgZGkgbGF2b3JvIGdpb3JuYWxpZXJpIGRlbGxlIG1hY2NoaW5lIiwKICAgICAgICAgICAgIHlheGlzLnJpZ2h0PUZBTFNFLAogICAgICAgICAgICAgY29sPSJibGFjayIpCgojIyBBZGQgbGluZXMKcHAxIDwtIGFkZFNlcmllcyhsaW4sIG9uID0gMSwgdHlwZSA9ICJsIiwgY29sID0gInJlZCIsIGx0eSA9IDEsIGx3ZCA9IDEsIHBjaCA9IDApCgojIyBBZGQgbGVnZW5kCnBwMSA8LSBhZGRMZWdlbmQoInRvcGxlZnQiLCAKICAgICAgICAgICAgICBsZWdlbmQubmFtZXM9YygicmVhbCIsICJwcmVkaWN0ZWQiKSwKICAgICAgICAgICAgICBjb2w9YygiYmxhY2siLCJyZWQiKSwKICAgICAgICAgICAgICBsdHk9YygxLDEpLAogICAgICAgICAgICAgIGx3ZD1jKDEsMSksCiAgICAgICAgICAgICAgbmNvbD0yLAogICAgICAgICAgICAgIGJnPSJ3aGl0ZSIpCgpwcDEKYGBgCgpgYGB7cn0Kc2NvcmVfVUNNMV90ZXN0IDwtIHNxcnQoIG1lYW4oIChwcmV2MS15XyleMiAsIG5hLnJtID0gVFJVRSApICkKcHJpbnQoc2NvcmVfVUNNMV90ZXN0KQpgYGAKCgpDcmVvIGlsIHNlY29uZG8gbW9kZWxsbywgc3RpbWFuZG8gaSB2YWxvcmkgaW5pemlhbGkgY29uIHVuYSByZWdyZXNzaW9uZSBBUig3KQpgYGB7cn0KI1ggPC0gWFssLTEzOi0xM10KI1hfIDwtIFhfWywtMTM6LTEzXQojWFggPC0gWFhbLC0xMzotMTNdCm1vZDIgPC0gU1NNb2RlbCh5IH4gWCArIGNzICsgc2kgKwogICAgICAgICAgICAgICAgICBTU010cmVuZCgxLCBOQSkrCiAgICAgICAgICAgICAgICAgIFNTTXNlYXNvbmFsKDYsIE5BLCAiZHVtbXkiKSwKICAgICAgICAgICAgICAgICAgU1NNc2Vhc29uYWwoMzEzLCBOQSwgInRyaWciLCBoYXJtb25pY3MgPSAxOjI0KSwKICAgICAgICAgICAgICAgIEggPSBOQSkKCiMgcGVyIGxlIGNvbmRpemlvbmkgaW5pemlhbGkgc3RpbW8gdW5hIHJlZ3Jlc3Npb25lIEFSKDcpCnJlZyA8LSBhcmltYSh5LCBjKDcsMCwwKSwgeHJlZyA9IGNiaW5kKGNzLCBzaSkpICMgUmVncmVzc2lvbmUgY29uIGVycm9yaSBBUih0KQpsZW5ndGgoY29lZmZpY2llbnRzKHJlZykpCmNmcyA8LSBjb2VmZmljaWVudHMocmVnKVs5OjQwXSAKCiMgSW1wb3N0byBsZSBjb25kaXppb25pIGluaXppYWxpCm1vZDIkYTFbMTM6NDRdIDwtIGNmcwptb2QyJGExWzQ1XSAgIDwtIHlbMV0gICAjcHJpbWEgb3NzZXJ2YXppb25lIChsZXZlbCkKbW9kMiRQMWluZiA8LSBtYXRyaXgoMCwgNTAsIDUwKSAjIEVsaW1pbmlhbW8gbGUgZGlzdHJpYnV6aW9uaSBkaWZmdXNlCgojIFBlciBsZSB2YXJpYW56ZSBpbml6aWFsaSB1c2lhbW8gbGUgdmFyaWFuemUgZGVnbGkgc3RpbWF0b3JpIGRpIHJlZ3Jlc3Npb25lCmRpYWcobW9kMiRQMVsxMzo0NCwgMTM6NDRdKSA8LSBkaWFnKHJlZyR2YXIuY29lZls5OjQwLCA5OjQwXSkKCiMgVXNpYW1vIGxhIHZhcmlhbnphIGRlbGxhIHNlcmllIHBlciBpbCBsaXZlbGxvICsgc2VhX2R1bW15CmRpYWcobW9kMiRQMVs0NTo1MCwgNDU6NTBdKSA8LSB2YXIoeSkKCmZpdDIgPC0gZml0U1NNKG1vZDIsIHJlcCgxMCwgMykpCmNhdCgiQ29kaWNlIGRpIGNvbnZlcmdlbnphID0iLCBmaXQyJG9wdGltLm91dCRjb252ZXJnZW5jZSkKYGBgCgpMYSBjb252ZXJnZW56YSBkZWxsZSBzdGltZSBkaSBtYXNzaW1hIHZlcm9zaW1pZ2xpYW56YSBudW1lcmljYSDDqCBhbmRhdGEgYSBidW9uIGZpbmUuIEZhY2NpYW1vIGlsIGdyYWZpY28gZGVsIHRyZW5kIChzbW9vdGhlZCkgc292cmFwcG9zdG8gYWxsYSBzZXJpZSBvcmlnaW5hbGUuCmBgYHtyfQpzbW8yIDwtIEtGUyhmaXQyJG1vZGVsLCBzbW9vdGhpbmcgPSAic3RhdGUiKQpwbG90KHRpbWVTZXJpZXMoeSwgYXMuRGF0ZSgiMjAxOS0wMS0wMSIpICsgMDoobGVuZ3RoKHkpLTEpKSkKbGluZXModGltZVNlcmllcyhzbW8yJGFscGhhaGF0WywgImxldmVsIl0sIGFzLkRhdGUoIjIwMTktMDEtMDEiKSArIDA6KGxlbmd0aCh5KS0xKSksCiAgICAgIGNvbCA9ICJyZWQiKQpgYGAKCmBgYHtyfQojIFJNU0UgT04gVFJBSU4KcHJlZGljdGlvbjJfdHJhaW4gPC0gc21vMiRhbHBoYWhhdFssICJsZXZlbCJdCnNjb3JlX1VDTTJfdHJhaW4gPC0gc3FydCggbWVhbiggKHByZWRpY3Rpb24yX3RyYWluLXkpXjIgLCBuYS5ybSA9IFRSVUUgKSApCnNjb3JlX1VDTTJfdHJhaW4KYGBgCgpgYGB7cn0gCnNtbzJfc2VhcyA8LSByb3dTdW1zKHNtbzIkYWxwaGFoYXRbMTozMTMsIHNlcSg0NiwgNTEsIDIpXSkgI2xhIHN0YWdpb25hbGl0YSBzaSDDqCBzcG9zdGF0YSBkYSAyMCBhIDUxCgpwbG90KHlbMTozMTNdLCB0eXBlID0gImwiKQpsaW5lcyhzbW8yJGFscGhhaGF0WzE6MzEzLCAibGV2ZWwiXSwgY29sID0gInJlZCIpCmxpbmVzKHNtbzJfc2Vhc1sxOjMxM10gKyBzbW8yJGFscGhhaGF0WzE6MzEzLCAibGV2ZWwiXSwgY29sID0gImJsdWUiKQpsaW5lcyhzbW8yX3NlYXNbMTozMTNdICsgc21vMiRhbHBoYWhhdFsxOjMxMywgImxldmVsIl0gKyBzbW8yJGFscGhhaGF0WzE6MzEzLCAic2VhX2R1bW15MSJdLCBjb2wgPSAiZ3JlZW4gIikgI3NlYV9kdW1teTEgw6ggbGEgc3RhZ2lvbmFsaXTDoCBhIGR1bW15IHN0b2Nhc3RpY2hlCmBgYApUcmVuZCBwaXUgc3RhZ2lvbmFsaXTDoCBpbiBibHUuClNvbG8gdHJlbmQgaW4gcm9zc28uCnRyZW5kIGUgc3RhZ2lvbmFsaXTDoCBvZ25pIHNldHRlIGdpb3JuaSBpbiB2ZXJkZS4KCmBgYHtyfQptb2QyXyA8LSBTU01vZGVsKGMoeSwgcmVwKE5BLCAxNzgpKSB+IFhYICsgY3NzICsgc2lpICsKICAgICAgICAgICAgICAgICBTU010cmVuZCgxLCBmaXQyJG1vZGVsJFFbMSwgMSwgMV0pKwogICAgICAgICAgICAgICAgIFNTTXNlYXNvbmFsKDYsIGZpdDIkbW9kZWwkUVsyLCAyLCAxXSwgImR1bW15IiksCiAgICAgICAgICAgICAgICAgU1NNc2Vhc29uYWwoMzEzLCBmaXQyJG1vZGVsJFFbMiwgMiwgMV0sICJ0cmlnIiwgaGFybW9uaWNzID0gMToyNCksCiAgICAgICAgICAgICAgICAgSCA9IGV4cChmaXQyJG9wdGltLm91dCRwYXJbM10pKQoKIyBQb25pYW1vIGFuY2hlIGxlIGNvbmRpemlvbmkgaW5pemlhbGkgY29tZSBuZWwgbW9kZWxsbyBwcmVjZWRlbnRlCm1vZDJfJGExIDwtIGZpdDIkbW9kZWwkYTEKbW9kMl8kUDEgPC0gZml0MiRtb2RlbCRQMQptb2QyXyRQMWluZiA8LSBmaXQyJG1vZGVsJFAxaW5mCgojIENhbGNvbGlhbW8gbG8gc21vb3RoaW5nIGRlbGxlIHZhcmlhYmlsaSBkaSBzdGF0byBlIGRlbCAic2VnbmFsZSIKc21vMl8gPC0gS0ZTKG1vZDJfLCBzbW9vdGhpbmcgPSBjKCJzdGF0ZSIsICJzaWduYWwiKSkKYGBgCgpgYGB7cn0KcHJldjIgPC0gc21vMl8kbXVoYXRbdGVzdCwgMV0KcHJldjIgPC0gaWZfZWxzZShwcmV2MiA8IDcwMCwgcHJldjIsIHByZXYyICsgNzAwKQoKCiMgQ29uZnJvbnRpYW1vIHByZXZpc2lvbmkgY29uIGRhdGkgcmVhbGkKbGluIDwtIHh0cyhwcmV2Miwgb3JkZXIuYnk9YXMuUE9TSVhjdChsb2FkW3Rlc3QsXSREYXRlKSwgZnJlcXVlbmN5ID0gNikKIyMgQ3JlYXRlIHBsb3Qgb2YgZGF0YXNldApwcDIgPC0gcGxvdC54dHModHMsIHNjcmVlbnM9MSwKICAgICAgICAgICAgIG1ham9yLnRpY2tzPSJtb250aHMiLAogICAgICAgICAgICAgbWFpbj0iUHJldmlzaW9uaSBlIGRhdGkgcmVhbGkgTWludXRpIGRpIGxhdm9ybyBnaW9ybmFsaWVyaSBkZWxsZSBtYWNjaGluZSIsCiAgICAgICAgICAgICB5YXhpcy5yaWdodD1GQUxTRSwKICAgICAgICAgICAgIGNvbD0iYmxhY2siKQoKIyMgQWRkIGxpbmVzCnBwMiA8LSBhZGRTZXJpZXMobGluLCBvbiA9IDEsIHR5cGUgPSAibCIsIGNvbCA9ICJyZWQiLCBsdHkgPSAxLCBsd2QgPSAxLCBwY2ggPSAwKQoKIyMgQWRkIGxlZ2VuZApwcDIgPC0gYWRkTGVnZW5kKCJ0b3BsZWZ0IiwgCiAgICAgICAgICAgICAgbGVnZW5kLm5hbWVzPWMoInJlYWwiLCAicHJlZGljdGVkIiksCiAgICAgICAgICAgICAgY29sPWMoImJsYWNrIiwicmVkIiksCiAgICAgICAgICAgICAgbHR5PWMoMSwxKSwKICAgICAgICAgICAgICBsd2Q9YygxLDEpLAogICAgICAgICAgICAgIG5jb2w9MiwKICAgICAgICAgICAgICBiZz0id2hpdGUiKQoKcHAyCmBgYAoKYGBge3J9CnNjb3JlX1VDTTJfdGVzdCA8LSBzcXJ0KCBtZWFuKCAocHJldjIteV8pXjIgLCBuYS5ybSA9IFRSVUUgKSApCnByaW50KHNjb3JlX1VDTTJfdGVzdCkKYGBgCgoKCgo8aDI+TU9ERUxMTyBBUklNQSBDT04gRk9VUklFUjwvaDI+CgpgYGB7cn0KbGlicmFyeSh4dHMpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkodHNlcmllcykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHVyY2EpCmxpYnJhcnkodHNvdXRsaWVycykKbGlicmFyeShmcHAyKQpsaWJyYXJ5KHRpbWVTZXJpZXMpCmxpYnJhcnkoS0ZBUykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KHRpbWV0aykKbGlicmFyeSh0aWR5cXVhbnQpCmxpYnJhcnkodGliYmxldGltZSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHJlY2lwZXMpCmxpYnJhcnkocnNhbXBsZSkKbGlicmFyeSh5YXJkc3RpY2spIApsaWJyYXJ5KGtlcmFzKQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KGRwbHlyKQpgYGAKCkFzc2VnbmFyZSBzdGFnaW9uYWxpdGEgYWxsYSBzZXJpZQpgYGB7cn0KdHNfdHJhaW4gPC0gbXN0cyh5LCBjKDYsIDI2LCAzMTMuMjUpKQp0c190ZXN0IDwtIG1zdHMoeV8sIGMoNiwgMjYsIDMxMy4yNSkpCmBgYAoKQ3JlbyBsYSBmdW56aW9uZSBwZXIgdmlzdWFsaXp6YXJlIEFjZiBlIFBhY2YgZSBsYSBhcHBsaWNvIGFsIGRhdGFzZXQsIHBlciB0cm92YXJlIGkgcGljY2hpLgpgYGB7cn0KYWNmX3BhY2YgPC0gZnVuY3Rpb24oeCwgbWF4LmxhZyl7CiAgcGFyKG1mcm93ID0gYygxLCAyKSkKICBBY2YoeCwgbWF4LmxhZykKICBQYWNmKHgsIG1heC5sYWcpCn0KYGBgCgpTdWxsYSBiYXNlIGRlaSB2YWxvcmkgb3R0ZW51dGkgcHJlY2VkZW50ZW1lbnRlLCBzYXJpbWEgMSwwLDEgIDEsMSwxLDYKYGBge3IsIGluY2x1ZGUgPVR9Cm1vZDEgPC0gQXJpbWEodHNfdHJhaW4sIGMoMSwwLDEpLCBsaXN0KG9yZGVyID0gYygxLDEsMSksIHBlcmlvZCA9IDYpKQptb2QxCmBgYAoKYGBge3IsIGluY2x1ZGUgPVR9CmFjZl9wYWNmKG1vZGxvZ2xpayRyZXNpZHVhbHMsIDM2KQpgYGAKCk9yYSBpbnRlZ3JvIGNvbiBpIHJlZ3Jlc3NvcmkgZGkgZm91cmllcjoKYGBge3IsIGluY2x1ZGUgPVR9Cm1vZDMgPC0gQXJpbWEodHNfdHJhaW4sIGMoNiwxLDIpLCBsaXN0KG9yZGVyID0gYygxLDAsMSksIHBlcmlvZCA9IDYpLCB4cmVnPWZvdXJpZXIodHNfdHJhaW4sIEs9YygzLCA5LCAyMCkpKQptb2QzCmFjZl9wYWNmKG1vZDMkcmVzaWR1YWxzLCAxMjApCmBgYAoKYGBge3J9CiNNQVBFIG9uIHRyYWluaW5nCnBsb3QodHNfdHJhaW4sIHR5cGUgPSAibCIpCmxpbmVzKG1vZDMkZml0dGVkLCBjb2wgPSAicmVkIikKbGVnZW5kKDEsIDk1LCBsZWdlbmQ9YygiRml0dGVkIiwgIlRyYWluIiksCiAgICAgICBjb2w9YygicmVkIiwgImJsYWNrIiksIGx0eT0xOjEsIGNleD0xKQpgYGAKCmBgYHtyfQojTUFQRSBvbiB0cmFpbmluZwpzY29yZV9BUklNQV90cmFpbiA8LSBzcXJ0KCBtZWFuKCAobW9kMyRmaXR0ZWQtYXMubnVtZXJpYyh0c190cmFpbikpXjIgLCBuYS5ybSA9IFRSVUUgKSApI3RyYWluX3BlcmYKc2NvcmVfQVJJTUFfdHJhaW4KYGBgCgpgYGB7ciwgaW5jbHVkZSA9VH0KcHJlX3RyYWluaW5nID0gZm9yZWNhc3QobW9kMywgeHJlZz1mb3VyaWVyKHRzX3Rlc3QsIEs9YygzLCA5LCAyMCksIDMxMyksIDMxMykKCiMgY29uZnJvbnRvIGNvbiBsYSBtZWRpYSBkZWdsaSB1bHRpbWkgdmFsb3JpIGRlbGxhIHNlcmllIHN0b3JpY2EgcmVhbGUuCiMgZWxldmF0byBhbCBxdWFkcmF0byBlIHBvaSBwcmVuZG8gbGEgcmFkaWNlIHF1YWRyYXRhIGRlbGxhIG1lZGlhLgpybXNlMSA8LSBzcXJ0KCBtZWFuKCAocHJlX3RyYWluaW5nJG1lYW4tYXMubnVtZXJpYyh0c190cmFpbikpXjIgLCBuYS5ybSA9IFRSVUUgKSApI3RyYWluX3BlcmYKCnByaW50KHJtc2UxKQpgYGAKCgpgYGB7ciwgaW5jbHVkZSA9VH0KcHJlMSA9IGZvcmVjYXN0KG1vZDMsIHhyZWc9Zm91cmllcih0c190ZXN0LCBLPWMoMywgOSwgMjApLCAxNzgpLCAxNzgpCgojIGNvbmZyb250byBjb24gbGEgbWVkaWEgZGVnbGkgdWx0aW1pIHZhbG9yaSBkZWxsYSBzZXJpZSBzdG9yaWNhIHJlYWxlLgojIGVsZXZhdG8gYWwgcXVhZHJhdG8gZSBwb2kgcHJlbmRvIGxhIHJhZGljZSBxdWFkcmF0YSBkZWxsYSBtZWRpYS4Kcm1zZTIgPC0gc3FydCggbWVhbiggKHByZTEkbWVhbi1hcy5udW1lcmljKHRzX3Rlc3QpKV4yICwgbmEucm0gPSBUUlVFICkgKSN0cmFpbl9wZXJmCgpwcmludChybXNlMikKYGBgCgpDYWxjb2xvIGlsIE1BUEUKCgoqKlBSRVZJU0lPTkkqKgpgYGB7cn0KYXV0b3Bsb3QocHJlMSkKYGBgCgpgYGB7cn0KcHJldl90cmFpbiA8LSBmb3JlY2FzdChwcmVfdHJhaW5pbmcsIHhyZWc9Zm91cmllcih0c190cmFpbiwgSz1jKDMsIDcsIDIwKSwgMzEzKSwgMzEzKQpwbG90KHByZXZfdHJhaW4pCgpwcmV2X3Rlc3QgPC0gZm9yZWNhc3QocHJlMSwgeHJlZz1mb3VyaWVyKHRzX3RyYWluLCBLPWMoMywgNywgMjApLCAxNzgpLCAxNzgpCnBsb3QocHJldl90ZXN0KQpgYGAKCmBgYHtyfQpwcmV0cmFpbiA8LSB0YWlsKHRzKHByZXZfdHJhaW4kbWVhbis0MDApLCAxNzQpCmxvd2VyOTV0cmFpbiA8LSB0YWlsKHByZXZfdHJhaW4kbG93ZXJbLDJdLCAxNzQpKzQwMAp1cHBlcjk1dHJhaW4gPC0gdGFpbChwcmV2X3RyYWluJHVwcGVyWywyXSwgMTc0KSArNDAwCnBsb3QodHMoeSksIHR5cGUgPSAibCIsIG1haW4gPSAiQVJJTUEgdHJhaW4iKQpsaW5lcyh0cyhwcmV2X3RyYWluJG1lYW4pLCBjb2w9InJlZCIpCmBgYAoKYGBge3J9CnByZSA8LSB0YWlsKHRzKHByZXZfdGVzdCRtZWFuKzQwMCksIDE3NCkKbG93ZXI5NSA8LSB0YWlsKHByZXZfdGVzdCRsb3dlclssMl0sIDE3NCkrNDAwCnVwcGVyOTUgPC0gdGFpbChwcmV2X3Rlc3QkdXBwZXJbLDJdLCAxNzQpICs0MDAKcGxvdCh0cyh5XyksIHR5cGUgPSAibCIsIG1haW4gPSAiQVJJTUEgdGVzdCIpCmxpbmVzKHRzKHByZXZfdGVzdCRtZWFuKSwgY29sPSJyZWQiKQpgYGAKCgpgYGB7cn0KI2V4cG9ydCBkYXRhCndyaXRlLmNzdihwcmUsIGZpbGU9cGFzdGUoZm9sZGVyX3Jpc3VsdGF0aSwgIi9wcmVkaWN0aW9uX2ZvdXJpZXIuY3N2Iiwgc2VwPSIiKSwgc2VwID0gIjsiLCBkZWMgPSAiLCIsIHJvdy5uYW1lcz1GQUxTRSkKd3JpdGUuY3N2KGxvd2VyOTUsIGZpbGU9cGFzdGUoZm9sZGVyX3Jpc3VsdGF0aSwgIi9sb3dlcl9mb3VyaWVyLmNzdiIsIHNlcD0iIiksIHNlcCA9ICI7IiwgZGVjID0gIiwiLCByb3cubmFtZXM9RkFMU0UpCndyaXRlLmNzdih1cHBlcjk1LCBmaWxlPXBhc3RlKGZvbGRlcl9yaXN1bHRhdGksICIvdXBwZXJfZm91cmllci5jc3YiLCBzZXA9IiIpLCBzZXAgPSAiOyIsIGRlYyA9ICIsIiwgcm93Lm5hbWVzPUZBTFNFKQpgYGAK